<?
/**
 * VoiceOne Webservices Methods
 *  
 * @package voiceone_webservices
 * @subpackage Zap
 */

//******************************************************************************
//******************           PUBLIC FUNCTIONS            *********************
//******************************************************************************
/****************************   ZAPATA CARDS  *********************************/
/**
* FUNCTION: getZapataCard
* Get zapata card(s) by id from database
* @param id:int -> Id of the zapata card, all if empty or zero
* @return:ZapCardArray -> Array of structure contains cards data
*/
function getZapataCard($id){
  global $db;
  $ret=array();
  //get zapata cards
  if((trim($id)!="")&&($id!=0)) $condition="AND `id`='".$id."'"; else $condition="";
  $qry="SELECT * FROM `cw_zapata_card` WHERE `active`='1' $condition ORDER BY `id`";
  $res=$db->Execute($qry);
  if($res!==false){
    while($arr=$res->FetchRow()){ 
      $ret[]=$arr;
  	}
  }
  //return
  return $ret;
}
/**
* FUNCTION: putZapataCard
* Insert zapata card into db (basic parameter) and update span count.
* To complete the card installation, we need more information like
* Port Type, Device, ... (see updateZapataPort)
* @param id:int -> Id of the zapata card
* @return:int -> card_id = ok, 0 = error (insert failed)
*/
function putZapataCard($id) {
  global $db;
  if((trim($id)=="")||($id==0)) return 0;
  //get basic card paramter (number of ports,driver load order,port types) 
  //and check if this is a span type card.
  $res=getZapataCard($id);
	if((!is_array($res))||(!is_array($res[0]))) return 0;
  $ports=$res[0]["ports"];
  $order=$res[0]["order"];
  $span=$res[0]["span"];
  $type=$res[0]["type"];
  $typeArr=split(",",$type);
  if(is_array($typeArr)) $typeDefault=$typeArr[0]; else $typeDefault="";
  //check if this card can configure the port type
  $options_type=split(',',$type);
  //read the id of the previous installed card 
  $qry="SELECT MAX(`card_id`) as lastid FROM `cw_zapata_port`";
  $res=$db->Execute($qry);
  if(($res!==false)&&($arr=$res->FetchRow())){
  	$card_id=$arr["lastid"];
  	if($card_id!="NULL") $card_id++; 
    else $card_id=1;
  }else return 0;
  //insert basic paramter for this card
  $res=true;
  for($i=1;$i<=$ports;$i++){
    if($typeDefault)
    $qry="INSERT INTO `cw_zapata_port` (`port`,`type`,`device`,`span`,`order`,`card`,`card_id`) ";
    $qry.="VALUES ('".string2sql($i)."','".string2sql($typeDefault)."','".string2sql("unused")."','".string2sql($span)."',";
    $qry.="'".string2sql($order)."','".string2sql($id)."','".string2sql($card_id)."')";
    $res&=$db->Execute($qry);
  }
	if($res===false) return 0;
  //next, set correct span numbers in database
  $spans=1; //spans start counting with 1
  $ports=getZapataPort(0);
  if(is_array($ports)){  
    foreach($ports as $p){
      $id=$p["id"];
      $span=$p["span"];
      if($span){
        $qry="UPDATE `cw_zapata_port` SET `span`='".$spans."' WHERE `id`='".$id."'";
        $db->Execute($qry);
        $spans++;
      }
    }
	}
  //return card_id
  return $card_id;
}
/**
* FUNCTION: delZapataCard
* Delete zapata card port(s), channel(s) by the card id
* @param id:int -> Id of the zapata card
* @return:int -> 1 = ok, 0 = error
* @todo perhaps we should refuse to delete a card if the card is inuse
*       say a 4 port E1 card with channel banks = ~ 120 Subscribers
*       so one command can delete most of the system ...
*/
function delZapataCard($id) {
  global $db;
  //check input parameter
  if((trim($id)=="")||($id==0)) return 0;
  if(!isZapataCardErasable($id)) return 0;
	//get card's ports
  $qry="SELECT * FROM `cw_zapata_port` WHERE `card_id`='".$id."'";
  $res=$db->Execute($qry);
	if($res!==false){
    while($arr=$res->FetchRow()){
      $ports[]=$arr;
  	}
  }
  //delete card's channels and ports
  if(is_array($ports)){ 
    foreach($ports as $p){
      $res=delZapataPort($p["id"]);
      if($res==0) return 0;
    }
  }
  //set correct span numbers in database
  $spans=1; //spans start counting at 1
  $all_ports=getZapataPort(0);
  if(is_array($all_ports)){ 
    foreach($all_ports as $p){
      if($p["span"]){
        $qry="UPDATE `cw_zapata_port` SET `span`='".$spans."' WHERE `id`='".$p["id"]."'";
        $res=$db->Execute($qry);
        if($res===false) return 0;
        $spans++;
      }
    }
  }
  //map local channels to zaptel channel numbers
	$zchannel=1;
  $channel=getZapataChannel(0);
  if(is_array($channel)){
    foreach($channel as $c){
      $qry="UPDATE `cw_zapata_channel` SET `zchannel`='".$zchannel."' WHERE `id`='".$c["id"]."'";
      $res=$db->Execute($qry);
      if($res===false) return 0;
      $zchannel++;
    }
  }
  //update zapata.conf and restart asterisk
  if(!writeZaptelInitConfFile()) return 0;
  //return
  return 1;
}
/**
* FUNCTION: isZapataCardErasable
* Verify if zapata card is erasable. If almost one channel of this card or another 
* card that has greater zchannel has been linked to an exstension or included in a 
* zapata port group, the card cannot be deleted because deleting the card all zchannel
* should be reloaded 
* @param id:int -> Id of the zapata card
* @return:int -> 1 = yes, 0 = no
*/
function isZapataCardErasable($id) {
  global $db;
  //check input parameter
  if((trim($id)=="")||($id==0)) return 0;
  //extract the lowest card zchannel
  $min_zchannel=0;
  $qry="SELECT MIN(zchannel) as min_zchannel ";
  $qry.="FROM `cw_zapata_channel` AS zc ";
  $qry.="JOIN `cw_zapata_port` AS zp ON (zp.`id`=zc.`port_id`) ";
  $qry.="WHERE zp.`card_id`='".$id."' ";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  $arr=$res->FetchRow();
  $min_zchannel=$arr["min_zchannel"];
  if($min_zchannel==0){
    //card hasn't been configured yet so we can delete it
    return 1;
  }
  //get the channel option id
  $channelOptionArr=getOptionByName("channel","ZAP");
  $idChannelOption=$channelOptionArr[0]["id"];
  //extract taken zchannel
  $qry="SELECT value ";
  $qry.="FROM cw_option_entity AS oe "; 
  $qry.="WHERE oe.option_id='".$idChannelOption."' ";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  while($arr=$res->FetchRow()){
    $vArr=split(",",$arr["value"]);
    if(is_array($vArr)){
      foreach($vArr as $v){
        //verify if almost one taken zchannel is greater or equal than card lowest zchannel
        if(trim($v)>=$min_zchannel) return 0;
      }
    }
  }
  //return erasable
  return 1;
}
/*****************************  ZAPATA PORTS  *********************************/
/**
* FUNCTION: getZapataPort
* Get zapata card port(s) by id (port_id)
* @param id:int -> Id of the zapata port
* @return:ZapPortArray -> Array of structure contains ports data
*/
function getZapataPort($id){
  global $db;
  $ret=array();
	//get zapata ports
  if((trim($id)!="")&&($id!=0)) $condition = "WHERE `id`='".$id."'"; else $condition="";
  // DO NOT CHANGE THIS QUERY: IT WILL RETURN THE CARDS, PORTS IN LOAD ORDER
  $qry="SELECT * FROM `cw_zapata_port` $condition ORDER BY `order`, `card_id`, `port`";
  $res=$db->Execute($qry);
  if($res!==false){
  	while($arr=$res->FetchRow()){
    	$ret[]=$arr;
		}
  }
  //return
  return $ret;
}
/**
* FUNCTION: updateZapataPort
* Update zapata card port(s) and tonezone(s)
* @param ports:ZapPortArray -> Array of Zapata port structure
* @param ports:ZapToneZoneArray -> Array of Zapata tone zone structure
* @return:int -> 1 = ok, 0 = error
*/
function updateZapataPort($ports,$zones) {
  global $db;
  $res=true;
  //check input parameters
  if((!is_array($ports))||(!is_array($zones))) return 0;
	//update tone zone
  $qry="UPDATE cw_zapata_zone SET `defaultzone`='0',`loadzone`='0'"; 
  $res=$db->Execute($qry);
  foreach($zones as $z){
    $loadzone=$z["loadzone"];
    $defaultzone =$z["defaultzone"];
    //set loadzone
    if(trim($loadzone)!=""){
    	$qry="UPDATE `cw_zapata_zone` SET `loadzone`='1' WHERE name='".$loadzone."'"; 
    	$res&=$db->Execute($qry);
		}
    //set default zone
    if(trim($defaultzone)!=""){
 			$qry="UPDATE `cw_zapata_zone` SET `defaultzone`='1', `loadzone`='1' WHERE name='".$defaultzone."'"; 
  		$res&=$db->Execute($qry);   
    }
  }
  if($res===false) return 0;
  // We need to have at least a db entry for each port and also 
  // a valid type per span port in the update request else we
  // refuse this update request as we cannot determine the channel
  // numbers correctly without E1/T1 specified.
  foreach($ports as $p){
  	//get port description
    $c=getZapataPort($p["id"]);
		if((!is_array($c))||(!is_array($c[0]))) return 0;
    $c=$c[0];
    //in order to compute channels properly we must have type E1, T1
    if(($c[0]["span"])&&($p["type"]!="E1")&&($p["type"]!="T1")){ 
      return 0;
    }
    //extract current port configuration
    $c_id      = $c[0]["id"];		// Port Id
    $c_port    = $c[0]["port"]; // Port Number
    $c_type    = $c[0]["type"];
    $c_device  = $c[0]["device"]; 
    $c_span    = $c[0]["span"];
    $c_timing  = $c[0]["timing"];
    $c_lbo     = $c[0]["lbo"];
    $c_framing = $c[0]["framing"];
    $c_coding  = $c[0]["coding"];
    $c_crc4    = $c[0]["crc4"];
    $c_yellow  = $c[0]["yellow"];
  	//extract new port configuration
    $p_id      = $p["id"];      // port_id from web update request
    $p_type    = $p["type"];    // E1,T1       - FXO,FXS
    $p_device  = $p["device"];  // PRI,E&M,FX? - fx?ks,fx?ls,fx?gs
    $p_timing  = $p["timing"];  // 0,1,2,3
    $p_lbo     = $p["lbo"];     // 0,1,2,3,4,5,6,7
    $p_framing = $p["framing"]; // ccs,cas,esf,d4
    $p_coding  = $p["coding"];  // hdb3,ami,b8zs
    $p_crc4    = $p["crc4"];    // 0,1
    $p_yellow  = $p["yellow"];  // 0,1
    //Init actions that will be done after all the paramter checks
    $createZapataChannel=false; // create channel for port_id
    $updateZapataChannel=false; // update channel for port_id
    $deleteZapataChannel=false; // delete channel for port_id
    $updateZapataPort   =false; // update port paramter
		/***********************/
		/***  FXO/FXS cards  ***/
		/***********************/
    if((trim($c_span)=="")||($c_span==0)){
    	//sanitize paramters  
      if(($p_type!="FXO")&&($p_type!="FXS")) return 0;
      //try to be smart about device names (must end in ks,ls,gs)
      if(strlen($p_device)){
      	$tmp="";
       	if(strtoupper($p_type)=="FXO"){$tmp="fxs"; $tmp.=substr($p_device,-2,2); }
        if(strtoupper($p_type)=="FXS"){$tmp="fxo"; $tmp.=substr($p_device,-2,2); }
        if(trim($tmp)!=""){
        	$p_device=$tmp;
        }
      }
      //check if we have a valid device for this port
      if(!in_array($p_device,array("fxsks","fxsls","fxsgs","fxoks","fxols","fxogs","unused"),true)){
        $p_device="unused"; //this should always work
      }
      //FXO,FXS cards don't use span type parameter
      $p_timing  = "";
      $p_lbo     = "";
      $p_framing = "";
      $p_coding  = "";
      $p_crc4    = "";
      $p_yellow  = "";
      //check if port has already channels (1 in this case: FX0/FXS)
      $channelArr=getZapataChannelFromPort($p_id);
      if((!is_array($channelArr))||(count($channelArr)!=1)){ // port has no channels
        $createZapataChannel=true;
        $updateZapataPort=true;
      }else{ 
        // port has one channel
        $channel=getZapataChannel($channelArr[0]);
        if((!is_array($channel))||(!is_array($channel[0]))) return 0;
        /**
         * Upload Zapata port and channel even if the port type and device haven't been modified
         */
        $channel[0]["type"]  = $p_type;
        $channel[0]["device"]= $p_device;
        switch($p_device) {
          case "fxoks": $channel[0]["signalling"]="fxo_ks"; break;
          case "fxols": $channel[0]["signalling"]="fxo_ls"; break;
          case "fxogs": $channel[0]["signalling"]="fxo_gs"; break;
          case "fxsks": $channel[0]["signalling"]="fxs_ks"; break;
          case "fxsls": $channel[0]["signalling"]="fxs_ls"; break;
          case "fxsgs": $channel[0]["signalling"]="fxs_gs"; break;
          default:      $channel[0]["signalling"]="";       break;
        }
        $updateZapataChannel = true;
        $updateZapataPort    = true;
        /*
      	//if port type is changed... 
        if($p_type!=$c_type){
        	//...if port inuse, do not change device type
        	if(!$channel[0]["available"]){
            continue;
          }else{
          	$channel[0]["type"]  = $p_type;
            $updateZapataChannel = true;
            $updateZapataPort    = true;
          }
        }
        //if port device is changed...
				if($p_device!=$c_device){
        	//...if port inuse, do not change device type
        	if(!$channel[0]["available"]){
          	continue;
          }else{
            //the FXO,FXS cards I know, can be ks,ls or gs. 
            //Find matching signalling for this device
            switch($p_device) {
              case "fxoks": $channel[0]["signalling"]="fxo_ks"; break;
              case "fxols": $channel[0]["signalling"]="fxo_ls"; break;
              case "fxogs": $channel[0]["signalling"]="fxo_gs"; break;
              case "fxsks": $channel[0]["signalling"]="fxs_ks"; break;
              case "fxsls": $channel[0]["signalling"]="fxs_ls"; break;
              case "fxsgs": $channel[0]["signalling"]="fxs_gs"; break;
              default:      $channel[0]["signalling"]="";       break;
            }
            $channel[0]["device"]= $p_device;
            $updateZapataChannel = true;
            $updateZapataPort    = true;
          }
        }
        */
      }
		}
    /********************/
		/***  SPAN cards  ***/
		/********************/
    else{
      //we already checked that we have a valid $p_type T1,E1
      //before going into the foreach ports as p loop
      //all depends on having a valid $p_type from here on.
      //now check if we have a valid device for this port
      if(($p_device!="PRI")&&($p_device!="E&M")&&($p_device!="FX?")){
        $p_device = NULL; // This should catch it all ...
      }
      //every valid device requires a valid span config
      if((!ctype_digit($p_timing))&&
      	 ( 
      		(!ctype_digit($p_lbo))||
         	(($p_framing!="ccs")&&($p_framing!="cas")&&($p_framing!="esf")&&($p_framing!="d4"))||
         	(($p_coding!="hdb3")&&($p_coding!="ami")&&($p_coding!="b8zs"))
         )
        ){
      	$p_device = NULL; //requested spanconfig cannot pass ztcfg test
      }else{
        //requested span config is valid, any changes to previous one
        if ($p_timing  != $c_timing  ||
            $p_lbo     != $c_lbo     ||
            $p_framing != $c_framing ||
            $p_coding  != $c_coding  ||
            $p_crc4    != $c_crc4    ||
            $p_yellow  != $c_yellow) {
          $updateZapataPort = true;
        } 
      }
      //Now we either have:
      //valid span config and valid device -> create valid device(s)
      //valid span config and no device    -> create device="unused"
      //   - or - both invalid             -> create device="unused"
      //check if port has already channels
      $channel=getZapataChannel($p_id);
			if((!is_array($channel))||(count($channel)==0)){
     		//no channels found
        $createZapataChannel = true;
        $updateZapataPort    = true;
      }else{
        //check is this span is inuse or now
        $spaninuse = 0;
        foreach($channel as $c){
          if($c["available"]!=1) $spaninuse++;
        }
				//if port type is changed...
        if($p_type!=$c_type){
        	//do not change port type if span is inuse
        	if($spaninuse!=0){
            continue;
          }else{
            $deleteZapataChannel = true;
            $createZapataChannel = true;
            $updateZapataPort    = true;
          }
        }
    		//if port device is changed...
        if($p_device!=$c_device){
        	//do not change device type if span is inuse
        	if($spaninuse!=0){
          	continue;
          }else{
            $deleteZapataChannel = true;
            $createZapataChannel = true;
            $updateZapataPort    = true;          
          }
        }
      }
    } 
    /*****************/
    /***  Updates  ***/
    /*****************/
    $updateZapataGroups =false; // update zapata groups
  	$writeZaptelInitFile=false; // write the new zaptel.conf, zaptel files
    //first update the port paramter
    if($updateZapataPort===true){
      $qry="UPDATE `cw_zapata_port` SET ";
      $qry.="`type`='".$p_type."', ";
      $qry.="`device`='".$p_device."', ";
      $qry.="`timing`='".$p_timing."', ";
      $qry.="`lbo`='".$p_lbo."', ";
      $qry.="`framing`='".$p_framing."', ";
      $qry.="`coding`='".$p_coding."', ";
      $qry.="`crc4`='".$p_crc4."', ";
      $qry.="`yellow`='".$p_yellow."' ";
      $qry.="WHERE `id`='".$p_id."'"; 
      $res=$db->Execute($qry);
      //if update success, set to write zaptel config file
      if(($res!=false)&&($db->Affected_Rows())){
        $writeZaptelInitFile=true;
      }
    }
		//update zapata channel if needed
    if($updateZapataChannel===true){
      $ret=updateZapataChannel($channel);
      if(updateZapataChannel($channel)){
        $writeZaptelInitFile=true;
      }
    }
		//delete zapata channel if needed
    if($deleteZapataChannel=== true){
      if(delZapataChannel($p_id)){
        $updateZapataGroups=true;
        $writeZaptelInitFile=true;
      }
    }
		//create zapata channel if needed
    if($createZapataChannel===true){
      if(putZapataChannel($p_id)){
        $updateZapataGroups=true;
        $writeZaptelInitFile=true;
      }
    }
  }
  //Now we have updated Ports and Channels but not Groups (the Zap Trunk) and 
  //Config file. So changes are only in voiceone db.
  //Note: currently we refuse to change anything important
  //      on ports or channels inuse, so I guess we do not
  //      have to expect channels disappearing that are used
  $ret=true;
  //update zapata groups if needed 
  if($updateZapataGroups===true){
    $groupArr=getZapataGroup(0);
		if(is_array($groupArr)){   
    	foreach($groupArr as $g){
        $ret=putZapataGroup($g,false);
        if($ret==0) return 0;
      }
    }
  }
	//write new config file if needed
  if($writeZaptelInitFile===true){
    if(!writeZaptelInitConfFile()){
      return 0;
    }
  }
	//return success
  return 1;
}
/**
* FUNCTION: getZapataPortOption
* Get zapata card port options by type (E1,T1,FXO,FXS)
* @param type:String -> Id of the zapata group, all if empty or zero
* @return:ZapPortOptionArray -> Array of structure contains port options data
*/
function getZapataPortOption($type) {
  global $db;
  $ret=array();
	//get port options
  if($type!="") $condition="WHERE `type`='".$type."'"; else $condition="";
  $qry="SELECT * FROM `cw_zapata_option` $condition";
  $res=$db->Execute($qry);
	if($res!==false){
  	while($arr=$res->FetchRow()){ 
    	$ret[]=$arr;
		}
  }
  //return
  return $ret;
}
/***************************  ZAPATA CHANNEL  *********************************/
/**
* FUNCTION: getZapataChannel
* Get zapata channel(s) channel order
* @param id:int -> Id of the zapata por, all if empty or zero
* @return:ZapChannelArray -> Array of structure contains zapata channels data
*/
function getZapataChannel($id){
  global $db;
  $ret=array();
  //get the channel option id
  $channelOptionArr=getOptionByName("channel","ZAP");
  $idChannelOption=$channelOptionArr[0]["id"];
	//get zapata channels
  if((trim($id)!="")&&($id!=0)) $condition="WHERE `id`='".$id."'"; else $condition="";
  // DO NOT CHANGE THIS QUERY IT WILL RETURN THE CHANNELS IN ZAPTEL ORDER
  $qry="SELECT * FROM `cw_zapata_channel` $condition ORDER BY `order`,`port_id`,`pchannel`";
  $res=$db->Execute($qry);
  if($res!==false){
  	while($arr=$res->FetchRow()){
      //check if available from isdn port group
      $qry="SELECT * FROM cw_zapata_portgroup WHERE port=".$arr["id"];
      $res2=$db->Execute($qry);
      if(($res2===false)||($res2->RecordCount()>0)) $arr["available"]=0;
      else{
      	$arr["available"]=1;
        //check if available from isdn extension
        $qry="SELECT value ";
        $qry.="FROM cw_client c ";
        $qry.="INNER JOIN cw_option_entity oe ON c.id=oe.entity_id "; 
        $qry.="WHERE oe.option_id='".$idChannelOption."' ";
        $qry.="AND c.technology=".ZAP;
        $res2=$db->Execute($qry);
        if($res2!==false){
          while($arrOpt=$res2->FetchRow()){
            $channelValue=$arrOpt["value"];
            if($arr["zchannel"]==$channelValue){
            	$arr["available"]=0;
            	break;
            }
          }
        }
      }
    	$ret[]=$arr;
  	}
  }
  //return
  return $ret;
}
/**
* FUNCTION: getZapataChannelFromPort
* Get zapata channel(s) id from zapata port id
* @param id:int -> Id of the zapata por, all if empty or zero
* @return:intArray -> Array of zapata channels id
*/
function getZapataChannelFromPort($id){
  global $db;
  $ret=array();
	//get zapata channels
  if((trim($id)!="")&&($id!=0)){ 
    // DO NOT CHANGE THIS QUERY IT WILL RETURN THE CHANNELS IN ZAPTEL ORDER
    $qry="SELECT id FROM `cw_zapata_channel` WHERE `port_id`='".$id."' ORDER BY `order`,`port_id`,`pchannel`";
    $res=$db->Execute($qry);
    if($res!==false){
    	while($arr=$res->FetchRow()){ 
      	$ret[]=$arr["id"];
    	}
    }
  }
  //return
  return $ret;
}


/*****************************  ZAPATA GROUP  *********************************/
/**
* FUNCTION: getZapataGroup
* Get zapata group(s) by id (group_id)
* @param id:int -> Id of the zapata group, all if empty or zero
* @return:ZapGroupArray -> Array of structure contains groups data
*/
function getZapataGroup($id){
  global $db;
	$ret=array();
  $count=0;
	//get zapata groups
  if((trim($id)!="")&&($id!=0)) $condition="WHERE `id`='".$id."'"; else $condition="";
  $qry="SELECT * FROM `cw_zapata_group` $condition ORDER BY 'id'";
  $res=$db->Execute($qry);
	if($res!==false){
    while($arr=$res->FetchRow()){
      //get channel info
      $channel=getChannel(getChannelFromEntity($arr["id"]));
      if((is_array($channel))&&(is_array($channel[0]))){
      	$arr["speeddial"]=substr($channel[0]["speeddial"],0,-1);
      	$arr["channel_order"]=$channel[0]["order"];
        if(is_array($channel[0]["cid"])){
        	$arr["cid"]=$channel[0]["cid"];
        }else{     // @TODO (1) delete else case
          $arr["cid"]=array(
          		"enable_cid" => $channel[0]["enable_cid"],
    					"cid_list" => $channel[0]["cid_list"],
              "cid_number" => $channel[0]["cid_number"],
              "cid_name" => $channel[0]["cid_name"],
              "cid_appexten" => $channel[0]["cid_appexten"],
              "force_cid" => $channel[0]["force_cid"],
              "clir" => $channel[0]["clir"]
          	);
        }
			}else{
      	//should not happen
      	$arr["speeddial"]="";
        $arr["channel_order"]="";
        $arr["cid"]=array();
      }
      //extract the ports, channels of the group
      $port=array();
      $qry="SELECT * FROM `cw_zapata_portgroup` WHERE `group`='".$arr["id"]."' ORDER BY 'id'";
      $res2=$db->Execute($qry);
			if($res2!==false){
        while($arr2=$res2->FetchRow()){
          $port[]=$arr2["port"];
        }
      }
      $arr["port"]=$port;
      //split the phonenumber of the group
      $arr["msns"]=split("\|",$arr["msns"]);
      //read the specific group option
      $arr["option"]=getEntityOption($id);
      //add group to return structure
      $ret[]=$arr;
    }
  }
  //return
  return $ret;
}
/**
* FUNCTION: putZapataCard
* Put zapata group
* @param group:ZapGroup -> Structure contains group data
* @return:int -> entity_id = ok, 0 = error (insert failed)
*/
function putZapataGroup($group,$reload=true){
  global $db;
  $writeZaptelInitFile=false;
  //delete group if already exists
  if((isset($group["id"]))&&($group["id"]!="")){    
    //save group data 
    $old_channel=getChannel(getChannelFromEntity($group["id"]));
    $channel_id=$old_channel[0]["id"];
    $channel_order=$old_channel[0]["order"];
    $entity["id"]=$group["id"];
    //delete group
    delZapataGroup($group["id"],false);
  }
  //get option array keys for signalling, channel, group
  $channelOptionArr    = getOptionByName("channel","ZAP");
  $channelOptionId     = $channelOptionArr[0]["id"];
  $contextOptionArr    = getOptionByName("context","ZAP");
  $contextOptionId     = $contextOptionArr[0]["id"];
  //get a new group number for zapata 0-63 if we don't have one already
  if(($group["group"]==0)||($group["group"]=="")){
    $qry="SELECT DISTINCT `group` FROM `cw_zapata_group` ORDER BY `group`";
    $res=$db->Execute($qry);
    if($res===false) return 0;
    if($res->RecordCount()==0){
    	//the first group set it as number one (save 0 for zap extensions)
    	$group["group"] = 1;
    }else{
    	//else retrieve the first free group number
      while($arr=$res->FetchRow()){
        $groups[]=$arr["group"];
      }
      for($zapgroup=1;$zapgroup<64;$zapgroup++){
        if(!in_array($zapgroup,$groups)){
          $group["group"]=$zapgroup;
          break;
        }
      }
      //zap groups are only valid from 0-63
      if($group["group"]>63) return 0;
    }
  }
  //extract group data
  $name       = $group["name"];
  $msns       = msns2string($group["msns"]);
  $ports      = $group["port"];
  $type       = $group["type"];
  $did        = $group["did"];
  $signalling = $group["signalling"];
  //find matching device for signalling
  if(($type=="FXO")||($type=="FXS")){
    switch($signalling) {
      case "fxs_ks":  $device="fxsks";  break;
      case "fxs_ls":  $device="fxsls";  break;
      case "fxs_gs":  $device="fxsgs";  break;
      case "fxo_ks":  $device="fxoks";  break;
      case "fxo_ls":  $device="fxols";  break;
      case "fxo_gs":  $device="fxogs";  break;
      default: return 0;
    }
  }
  //check if we have a port array to group
  if((!is_array($ports))||(count($ports)==0)) return 0;
  //create a new entity
  $entity["key"]="0";
  $entity["type"]=ZAPATA_GROUP;
  $entity_id=putEntity($entity);
  if((is_object($entity_id))||($entity_id==0)) return 0;
  //extract the rule set ID
  $rule=extractRuleFromOption($group["option"],ZAP);
  //insert group in db
  $qry="INSERT INTO cw_zapata_group (`id`,`group`,`name`,`rule`,`msns`,`type`,`did`,`signalling`) ";
  $qry.="VALUES ($entity_id,'".$group["group"]."','".$name."','".$rule."','".$msns."','".$type."','".$did."','".$signalling."')";
  $res=$db->Execute($qry);
  if($res===false) return 0; 
  //put the new channel
  $channel=array(
      "id" => $channel_id,
      "order" => $channel_order,
      "technology" => ZAP,
      "resource" => "g".$group["group"],
      "key" => $entity_id,
      "msns" => $group["msns"],
      "rule" => $rule,
      "trunk" => 1,
      "did" => $did,
      "speeddial" => $group["speeddial"],
      //"cid" => $group["cid"]      // @TODO (2) decomment and delete others cid parameters
      "cid_list" => $group["cid"]["cid_list"],
      "enable_cid" => $group["cid"]["enable_cid"],
      "cid_number" => $group["cid"]["cid_number"],
      "cid_name" => $group["cid"]["cid_name"],
      "cid_appexten" => $group["cid"]["cid_appexten"],
      "force_cid" => $group["cid"]["force_cid"],
      "clir" => $group["cid"]["clir"]
    );
  $channelId=putChannel($channel);  
  //expand PRI and E&M ports
  if(($type=="PRI")||($type=="E&M")){
    foreach($ports as $p) {
      $qry="SELECT `port_id` FROM `cw_zapata_channel` WHERE `id`='".$p."' ORDER BY `order`,`port_id`,`pchannel`";
      $res=$db->Execute($qry);
      if($res===false) return 0;
      while($arr=$res->FetchRow()){
      //Note: This fixes the port_group dup's when
      //      installing, deleting cards
      // $port_id[]=$arr["port_id"];
        $port_id_list[]=$arr["port_id"];
      }
      $port_id=array_unique($port_id_list);
    }
    if(!is_array($port_id)) return 0;
    //Now get all channels of device type dchan or e&m 
    //This should filter out the "unused" and "dchan"
    //We may need this info later to implements adding 
    //and deleting channels for fractional E1,T1 
    foreach($port_id as $p){
      $qry="UPDATE `cw_zapata_channel` SET ";
      $qry.="`group`='".$name."', ";
      $qry.="`signalling`='".$signalling."', ";
      $qry.="`available`='0' ";
      $qry.="WHERE `port_id`='".$p."' ";
      $qry.="AND (`device`='bchan' OR `device`='e&m')";
      $res=$db->Execute($qry);
      if($res===false) return 0;
      $qry="SELECT `id` ";
      $qry.="FROM `cw_zapata_channel` ";
      $qry.="WHERE `port_id`='".$p."' ";
      $qry.="AND (`device`='bchan' OR `device`='e&m') ";
      $qry.="ORDER BY `order`,`port_id`,`pchannel`";
      $res=$db->Execute($qry);
      if($res===false) return 0;
      while($arr=$res->FetchRow()){
        $port[]=$arr["id"];
      }
    }
    $ports=$port; //new expanded portlist
  } 
  //FXO/FXS type
  else { 
    foreach($ports as $p){
      //update port
      $port_idArr=getZapataGroupFromChannel($p);
      if(is_array($port_idArr)){
        foreach($port_idArr as $port_id){
          $qry="UPDATE `cw_zapata_port` SET `device`='".$device."'  WHERE `id`='".$port_id."'";
          $res=$db->Execute($qry);
			    if($res===false) return 0;
        }
      }
      //update channel
      $ret=getZapataChannel($p);
      if((is_array($ret))&&(is_array($ret[0]))){
        if($ret[0]["signalling"]!=$signalling){
          $writeZaptelInitFile = true;   //update zaptel.conf as well
        }
      }
      $qry="UPDATE `cw_zapata_channel` SET `group`='".$name."', `signalling`='".$signalling."', `device`='".$device."', `available`='0' WHERE `id`='".$p."'";
      $res=$db->Execute($qry);
			if($res===false) return 0;
    }
  }
  //write the ports into portgroup table
  $res=true;
  foreach($ports as $p) { // port = id from cw_zapata_channel table
    $qry="INSERT INTO cw_zapata_portgroup (`port`,`group`) VALUES ('".$p."','".$entity_id."')";
    $res&=$db->Execute($qry);
  }
	if($res===false) return 0;
  // Problem: cw_option_entity as well as putEntityOption are not well
  //          equipped to handle zapata, so we can only use a key once
  //          per entity. This breaks grouping of FXO, FXS ports if
  //          they use different devices hmmm ....
  
  // Build simple Channel List until cw_entity_option is sorted out
  $portList="";
  $zchannel="";
  $schannel="";
  $arrPort=getZapataChannel(0);  //read all the installed port
  if(is_array($arrPort)){
    foreach($arrPort as $p){
      if(@in_array($p["id"],$ports,true)){ // Found a Channel List Member
        //start of portList with the 1st channel
        if($portList==""){
           $portList = $p["zchannel"];
           $zchannel = $p["zchannel"];
           $schannel = $zchannel;
           continue;
        }
        //are channels in sequence?
        if(($p["zchannel"]-$zchannel)==1){
          $zchannel = $p["zchannel"];
          continue;
        }else{ 
          if($zchannel != $schannel){
            $portList.="-";
            $portList.=$zchannel; 
          }
          $portList.= ",";
          $portList.= $p["zchannel"];
          $zchannel = $p["zchannel"];
          $schannel = $zchannel;
        }
      }
    }
  }
  if($zchannel != $schannel){
    $portList.="-";
    $portList.=$zchannel; 
  }
  //add channel list to group option array
  $group["option"][]=array("key"=>$channelOptionId, "value"=>$portList);
  putEntityOption($group["option"],ZAP,$entity_id,$group);
  //update incoming context in ast_config for this trunk group
  $context = "trunk_".$channelId;
  $qry="UPDATE `ast_config` SET `var_val`='".$context."' WHERE `filename`='zapata.conf' AND `category`='channels' AND `var_name`='context' AND `zsection`='".$name."'";
  $res=$db->Execute($qry);
	if($res===false) return 0;
  //write the global variables
  $res=generateGlobalVar();
  if($res===false) return 0;
  //write a new zaptel.conf file, if needed
  if($writeZaptelInitFile===true){
    if(!writeZaptelInitConfFile()) {
      return 0;
    }
  }
  // else reload chan_zap
  else if($reload!==false){
  	reloadZapDriver();
  }
  //return
  return $entity_id;
}
/**
* FUNCTION: delZapataGroup
* Delete zapata group and return channels into pool
* @param id:int -> Id of the Zapata group to delete
* @return:int -> 1 = ok, 0 = error
*/
function delZapataGroup($id,$reload=true) {
  global $db;
  //check input parameter
	if((trim($id)=="")||($id==0)) return 0;
  //retrieve the group name
  $group=getZapataGroup($id);
  if((!is_array($group))||(!is_array($group[0]))) return 0;
  $name=$group[0]["name"];
	//set channels available
  $qry="UPDATE `cw_zapata_channel` SET `group`='', `available`='1' WHERE `group`='".$name."'";
  $res=$db->Execute($qry);
	if($res===false) return 0;
  //remove the options written in the asterisk table
  delEntityOption($id, ZAP);
  //delete channel
  $channelId=getChannelFromEntity($id);
  if(($reload===true)&&($channelId!=0)) delChannel($channelId,false);
  //delete from zapata_portgroup
  $qry="DELETE FROM cw_zapata_portgroup WHERE `group`='".$id."'";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  //delete group
  $qry="DELETE FROM cw_zapata_group WHERE id='$id'";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  $ret=delEntity($id);
	//reload chan_zap
  if($reload!==false) {
		reloadZapDriver();
  }
  //return
  return 1;
}
/***************************  ZAPATA TONE ZONE  *******************************/
/**
* FUNCTION: getZapataToneZone
* Get zapata tonezone(s), ordered by name
* @param id:int -> Id of the Zapata Tone Zone
* @return:ZapToneZoneArray -> Array of Zap Tone Zone data structure
*/
function getZapataToneZone($id){
  global $db;
  $ret=array();
  //get tone zones
  if((trim($id)!="")&&($id!=0)) $condition="WHERE `id`='".$id."'"; else $condition="";
  $qry="SELECT * FROM `cw_zapata_zone` $condition ORDER BY `name`";
  $res=$db->Execute($qry);
	if($res!==false){
    while($arr=$res->FetchRow()){ 
      $ret[]=$arr;
  	}
  }
  //return
  return $ret;
}
//******************************************************************************
//*****************           PRIVATE FUNCTIONS            *********************
//******************************************************************************
/**
* PRIVATE FUNCTION: delZapataPort
* Delete zapata port
* @param id:int -> Id of the zapata port
* @return:int -> 1 = ok, 0 = error
*/
function delZapataPort($id){
	global $db;
  //check input parameter
	if((trim($id)=="")||($id==0)) return 0;
  //delete channels from cw_zapata_portgroup
  $channelArr=getZapataChannelFromPort($id);
  if(is_array($channelArr)){
    foreach($channelArr as $c_id){
      //delete portgroup
      $qry="DELETE from `cw_zapata_portgroup` WHERE `port`='".$c_id."'"; 
      //delete channels
      $res=delZapataChannel($c_id);
      if($res==0) return 0;   
    }
  }
	//delete port
  $qry="DELETE FROM `cw_zapata_port` WHERE `id`='".$id."'"; 
  $res=$db->Execute($qry);
  if($res===false) return 0;
	//return
  return 1;
}
/**
* PRIVATE FUNCTION: putZapataChannel
* Put zapata channel(s) into database
* @param id:int -> Id of the zapata port linked to the new channel
* @return:int -> number of created channels
*/
function putZapataChannel($id){
  global $db;
  //Init return: number of channels created
  $ret=0;
  //check input parameter
  if((trim($id)=="")||($id==0)) return 0;
  //get port info
  $ports=getZapataPort($id); // Fetch port information
  if((!is_array($ports))||(!is_array($ports[0]))) return 0;
  $p=$ports[0];
  $id=$p["id"];
  $order=$p["order"];
  //check if Port already has channels
  $channel=getZapataChannelFromPort($p["id"]);
  if((is_array($channel))&&(count($channel)>0)){ 
    return 0;
  }
  //set number of channels to create for this type of port
  switch($p["type"]){
    //SPAN CARDS
    case "E1":  $type=$p["type"]; $channels=31; break;
    case "T1":  $type=$p["type"]; $channels=24; break;
    //FXO,FXS
    case "FXO": $type=$p["type"]; $channels=1;  break;
    case "FXS": $type=$p["type"]; $channels=1;  break;
    case "FX?": $type=$p["type"]; $channels=1;  break;
    //Unknown
    default:    $type=$p["type"]; $channels=0;  break;
  }
  if($channels==0) return 0; 
  //set default device type(s) for zaptel.conf
  $dchan=0;
  switch($p["device"]){
    //supported Span devices
    case "PRI": 
    	$type="PRI"; $device="bchan"; $signalling="";  
    	if($p["type"]=="E1") $dchan=16; 
      else if($p["type"]=="T1") $dchan=24;
      break;
    case "E&M": $type="E&M"; $device="e&m";    $signalling=""; 	break;
    case "FX?":	$type="FX?"; $device="unused"; $signalling="";  break;
    //supported FXO,FXS
    case "fxsks":  $device=$p["device"]; $signalling="fxs_ks";  break;
    case "fxsls":  $device=$p["device"]; $signalling="fxs_ls";  break;
    case "fxsgs":  $device=$p["device"]; $signalling="fxs_gs";  break;
    case "fxoks":  $device=$p["device"]; $signalling="fxo_ks";  break;
    case "fxols":  $device=$p["device"]; $signalling="fxo_ls";  break;
    case "fxogs":  $device=$p["device"]; $signalling="fxo_gs";  break;
    case "unused": $device=$p["device"]; $signalling="";        break;
    //unknown device, e.g. span w/o valid device
    default:       $device="unused";     $signalling="";        break;
  }
  //insert channels in db
  for($pchannel=1; $pchannel<=$channels; $pchannel++){
    $qry="INSERT INTO `cw_zapata_channel`(`port_id`,`order`,`group`,`type`,`device`,`signalling`,`pchannel`,`zchannel`,`available`) ";
    $qry.="VALUES ('".$id."','".$order."','','".$type."','".$device."','".$signalling."','".$pchannel."','0','1')";
    $res=$db->Execute($qry);
    if(($res!==false)&&($db->Affected_Rows())){
    	$ret++;
    }else return 0;
  }
	//set dchannel if the card has a dchannel
  if($dchan>0){ 
    $qry="UPDATE `cw_zapata_channel` SET `device`='dchan' WHERE `port_id`='".$id."' AND `pchannel`='".$dchan."'";
    $res=$db->Execute($qry);
    if($res===false) return 0;
  }
  //map local channels to zaptel channel numbers
  $channel=getZapataChannel(0);
  if(is_array($channel)){
    $zchannel = 0;
    foreach($channel as $c){
      $zchannel++;    
      $qry="UPDATE `cw_zapata_channel` SET `zchannel` = '".$zchannel."' WHERE `id` = '".$c["id"]."'";
      $res=$db->Execute($qry);
      if($res===false) return 0;
    }
	}
  //return 
  return $ret; 
}
/**
* PRIVATE FUNCTION: delZapataChannel
* Delete zapata channel
* @param id:int -> Id of the zapata channel
* @return:int -> 1 = ok, 0 = error
*/
function delZapataChannel($id) {
  global $db;
  $ret=0;
  //check input parameter
	if((trim($id)=="")||($id==0)) return 0;
	//delete channel
  $qry="DELETE FROM `cw_zapata_channel` WHERE `id`='".$id."'";
  $res=$db->Execute($qry);
  if($res===false) return 0;
	//return
  return 1;
}
/**
* PRIVATE FUNCTION: updateZapataChannel
* Update zapata channel(s)
* @param channel:ZapChannelArray -> Array of Zapata channel structure
* @return:int -> 1 = ok, 0 = error
*/
function updateZapataChannel($channel) {
  global $db;
  $ret=0;
	//update channels
  if(is_array($channel)){
    foreach($channel as $c){
      $qry="UPDATE `cw_zapata_channel` SET ";
      $qry.="`order`='".$c["order"]."', ";
      $qry.="`group`='".$c["group"]."', ";
      $qry.="`type`='".$c["type"]."', ";
      $qry.="`device`='".$c["device"]."', ";
      $qry.="`signalling`='".$c["signalling"]."', ";
      $qry.="`pchannel`='".$c["pchannel"]."', ";
      $qry.="`zchannel`='".$c["zchannel"]."', ";
      $qry.="`available`='".$c["available"]."' ";
      $qry.="WHERE `id`='".$c["id"]."'";
      $res=$db->Execute($qry);
			if($res===false) return 0;
    }
  }
  //return
  return 1;
}
/**
* FUNCTION: getZapataGroupFromChannel
* Get zapata group(s) by channel id
* @param id:int -> Id of the zapata channel
* @return:intArray -> Array of port id
*/
function getZapataGroupFromChannel($id){
  global $db;
	$ret=array();
	//get zapata groups
  if((trim($id)=="")||($id==0)) return 0; 
  $qry="SELECT * FROM `cw_zapata_channel` WHERE `id`='".$id."' ";
  $res=$db->Execute($qry);
	if($res!==false){
    while($arr=$res->FetchRow()){
      $ret[]=$arr["port_id"];
    }
  }
  //return
  return $ret;
}
/**
* PRIVATE FUNCTION: reloadZapDriver
* reload chan_zap in asterisk
* @param null
* @return:null
*/
function reloadZapDriver(){
  //reload the chan_zap
  sendCommand("unload chan_zap.so");
  sleep(2);
  sendCommand("load chan_zap.so");
  sleep(2);
  sendCommand("reload");
  sleep(3);
}
/**
* PRIVATE FUNCTION: reloadZapDriver
* Write configuration file zaptel.conf and driver load order zaptel
* @param null
* @return:bool -> If success method returns true, else it returns false
*/
function writeZaptelInitConfFile(){
  global $db,$voiceoneWSDocumentRoot,$voTools;
  //----------------------------------------------------------------------------
  // Sections of config file: zaptel.conf   |  Sections of module file: zaptel
  // ------------------------------------   |  -------------------------------
  // 1 = header information                 |  1 = header information 
  // 2 = span config                        |  2 = TELEPHONEY=yes
  // 3 = span channel config                |  3 = # DEBUG=yes
  // 4 = FXO,FXS cards                      |  4 = MODULES="module1 module2"
  // 5 = Tonezone                           |	 
  //----------------------------------------------------------------------------
  $pspans=""; // write the span config section
  $pchans=""; // write the chan config section
  $pother=""; // write the fxos config section
  $ptones=""; // write the tone config section
  $kmodule_last="";
  $kmodule_list="";
  $card_id_last="";
  //file update timestamp
  $update=date("r");
	//set EOF
  $EOF="\n";
	//init zaptel.conf files
  $zaptel=""; // zaptel.conf = zaptel config file
  $zaptel.="##################################################".$EOF;
  $zaptel.="# VoiceOne Configurator ".$EOF;
  $zaptel.="# file: zaptel.conf     ".$EOF;
  $zaptel.="# date: $update         ".$EOF;
  $zaptel.="##################################################".$EOF;
  $zaptel.="#\n";
  //init zaptel driver file
	$driver=""; // zaptel = driver load order
  $driver.="##################################################".$EOF;
  $driver.="# VoiceOne Configurator ".$EOF;
  $driver.="# file: zaptel          ".$EOF;
  $driver.="# date: $update         ".$EOF;
  $driver.="##################################################".$EOF;
  $driver.="#".$EOF;
  $driver.="TELEPHONY=yes".$EOF;
  $driver.="# DEBUG=yes".$EOF.$EOF;
	//extract card list
  $cards=getZapataCard(0);
  //extract port list
  $ports=getZapataPort(0);
	//process port list
  if(is_array($ports)){
    foreach($ports as $p){
      $id      = $p["id"];
      $port    = $p["port"];
      $type    = $p["type"]; 
      $device  = $p["device"];
      $span    = $p["span"]; 
      $timing  = $p["timing"];
      $lbo     = $p["lbo"];
      $framing = $p["framing"];
      $coding  = $p["coding"];
      $crc4    = $p["crc4"];
      $yellow  = $p["yellow"];
      $card    = $p["card"];
      $card_id = $p["card_id"];
			//adapt port parameters
      if($crc4)   $crc4=",crc4";     else $crc4="";
      if($yellow) $yellow=",yellow"; else $yellow="";
      //write current card name into zaptel.conf sections and kmodule card name into zaptel
      if($card_id!=$card_id_last){
        //get card kmodule name and add to driver list
        $kmodule=$cards[$card-1]["driver"];
        if(strcmp($kmodule,$kmodule_last)!=0){
          if(strlen($kmodule)>0){
            //add driver to driver list
            $kmodule_list.=$kmodule." ";
          }
        }
        //get card name for zaptel.conf section(s)
        $cardnm = $cards[$card-1]["name"];
        if($span){
          $pspans.=$EOF.$EOF."# $cardnm".$EOF.$EOF;
          $pchans.=$EOF.$EOF."# $cardnm".$EOF.$EOF;
        }else{
          $pother.=$EOF.$EOF."# $cardnm".$EOF.$EOF;
        }
      }
      /**
       * SPAN CARD (multiple channels)
       */
      if($span){ 
        //check if we have a valid span configuration
        if (!ctype_digit($timing) && !ctype_digit($lbo) ||
           ($framing != "ccs" && $framing != "cas" && 
            $framing != "esf" && $framing != "d4") ||
           ($coding != "hdb3" && $coding  != "ami" && $coding != "b8zs")) {
          $pspans.="# ".$EOF; //span config invalid = comment out 
        }
        $pspans.="span=".$span.",".$timing.",".$lbo.",".$framing.",".$coding.$crc4.$yellow.$EOF;
				//get Channel info
        $channelArr=getZapataChannelFromPort($id);
        //do we also need to validate the channel config?
        //guess it should be ok as we validate it already
        //when we setup the channel config ....
        if(!is_array($channelArr)){
          //no channels found that should never happen
          $pchans.=$EOF."# Port: ".$port." - No channel config found.";
        }else{
          $devicename=""; // Start with no name
          $zchannel=0;
          foreach($channelArr as $channel_id){
            $c=getZapataChannel($channel_id);
            //is this a new device for the channel list?
            if(strcmp($devicename,$c["device"])!=0){
              //is this the end of a device list?
              if($devicename!=""){ // Skip first channel
                //had the last list more than one channel ?
                if(($c["zchannel"]-$zchannel)==1){
                  $pchans.="-";
                  $pchans.=$zchannel; 
                }
              }
              $devicename = $c["device"];
              $pchans.=$EOF."$devicename";
              $pchans.="=";
              $pchans.=$c["zchannel"];
            } else {
              //still same device for this channel
              $zchannel=$c["zchannel"];
            }
          }  

          // Catch the last device list
          if (($c["zchannel"]-$zchannel) == 0) {
            $pchans.="-";
            $pchans.=$zchannel; 
          }
        } // End span channels

      } 
      /**
       * FXO/FXS CARD (only have one channel per port)
       */
      else{
        $channelArr=getZapataChannelFromPort($id);
        if(!is_array($channelArr)){
          // No channels found that should never happen
          $pother.="# Port: $port - No channel config found.".$EOF;
        }else{
          $channel=getZapataChannel($channelArr[0]);
          $zchannel=$channel[0]["zchannel"];
          $device  =$channel[0]["device"];
          $pother.="$device=$zchannel".$EOF;
        }
      }
     	//set module and card as last
      $kmodule_last = $kmodule;
      $card_id_last = $card_id;
    }
  }
  //add tone section for zaptel.conf
  $ptones.=$EOF.$EOF."# ZAPTEL (Tonezone) ".$EOF;
  $tones=getZapataToneZone(0);
  if(is_array($tones)) {
    foreach ($tones as $t) {
      $loadzone=$t["loadzone"];
      $defz=$t["defaultzone"];
      $name=$t["name"];
      if($loadzone=="1"){
        $loadzone_list.="loadzone=".$name.$EOF;
      }
      if($defz=="1") {
        $defaultzone=$name.$EOF;
      }
    }
  }
  if(strlen($defaultzone)==0) $defaultzone="us";
  $ptones.=$loadzone_list.$EOF;
  $ptones.="defaultzone=".$defaultzone.$EOF;
  //merge Sections for zaptel.conf
  $zaptel.=$pspans;
  $zaptel.=$pchans;
  $zaptel.=$pother;
  $zaptel.=$ptones;
  //merge kmodule_list zaptel
  $driver.="MODULES=\"";
  if(strlen($kmodule_list)){
    $driver.=$kmodule_list;
  }else{
    $driver.="ztdummy ";
  }  
  $driver=substr($driver,0,-1);
  $driver.= "\"".$EOF.$EOF;
  //write zaptel.conf into sandbox 
  $filename1=$voiceoneWSDocumentRoot."/sandbox/zaptel.conf";
  $ret=@file_put_contents($filename1,$zaptel); 
	if($ret===false) return false;
  //write zaptel into sandbox
  $filename2=$voiceoneWSDocumentRoot."/sandbox/zaptel";
  $ret=file_put_contents($filename2,$driver); 
	if($ret===false) return false;
  //move the zaptel configuration files in config dir
  exec("sudo $voTools zaptelWriteConf $filename1 $filename2",$out);
	//turn off asterisk
  pbxSwitch("stop");
	//restart Zap and wait it goes up
  exec("sudo $voTools zaptel_init stop",$out);
  sleep(1);
  exec("sudo $voTools zaptel_init start",$out);
  sleep(1);
  exec("sudo $voTools zaptel_init restart",$out);
  sleep(3);
  //return success
  return true;
}
?>